package bancosys.tec.datadigester.processor.impl;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import jmine.tec.datadigester.DataDigesterMessages;
import jmine.tec.executionlog.ExecutionLogController;
import jmine.tec.utils.collection.Mapper;
import jmine.tec.utils.collection.MapperUtils;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;

import bancosys.tec.datadigester.DataDigesterException;
import bancosys.tec.datadigester.fileregistry.dao.FileRegistryDAO;
import bancosys.tec.datadigester.fileregistry.domain.FileRegistry;
import bancosys.tec.datadigester.processor.AbstractProcessor;
import bancosys.tec.datadigester.processor.Processor;
import bancosys.tec.datadigester.processor.ProcessorFactory;
import bancosys.tec.persist.dao.BeanNotFoundException;

/**
 * @author piercio
 */
public class DatabaseProcessorFactory implements ProcessorFactory, BeanFactoryAware {

    private final Map<String, Processor> processorMap = new HashMap<String, Processor>();

    private ExecutionLogController controller;

    private volatile BeanFactory beanFactory;

    /**
     * {@inheritDoc}
     */
    public Processor createProcessor(String fileCode) {
        Processor processor = this.processorMap.get(fileCode);
        if (processor == null) {
            processor = this.loadProcessor(fileCode);
            this.processorMap.put(fileCode, processor);
        }
        return processor;
    }

    /**
     * Encontra o processor para o dado arquivo.
     * 
     * @param fileCode o código do arquivo.
     * @return o processor para o dado arquivo.
     */
    private Processor loadProcessor(String fileCode) {
        FileRegistryDAO dao = this.controller.getDAOFactory().getDAOByClass(FileRegistryDAO.class);

        FileRegistry file = null;
        try {
            file = dao.findByFileCode(fileCode);
        } catch (BeanNotFoundException e) {
            throw new DataDigesterException(DataDigesterMessages.FILE_REGISTRY_NOT_FOUND.create(fileCode), e);
        }
        Processor processor = (Processor) this.beanFactory.getBean(file.getProcessorId());
        if (processor == null) {
            throw new DataDigesterException(DataDigesterMessages.NO_PROCESSOR_FOR_CODE_WITH_NAME.create(fileCode, file.getProcessorId()));
        }
        // FIXME (piercio) Garante que todos os AbstractProcessors tenham um executionLogController, mesmo que não seja definido no Spring.
        if (processor instanceof AbstractProcessor) {
            ((AbstractProcessor) processor).setExecutionLogController(this.controller);
        }
        return processor;
    }

    /**
     * @param executionLogController o valor para o campo controller
     */
    public void setExecutionLogController(ExecutionLogController executionLogController) {
        this.controller = executionLogController;
    }

    /**
     * {@inheritDoc}
     */
    public void setBeanFactory(BeanFactory beanFactory) {
        this.beanFactory = beanFactory;
    }

    /**
     * {@inheritDoc}
     */
    public List<? extends String> getAvailableProcessors() {
        List<FileRegistry> list = this.controller.getDAOFactory().getGenericDAO(FileRegistry.class).findAll();
        return MapperUtils.mapList(list, new Mapper<FileRegistry, String>() {

            public String apply(FileRegistry input) {
                return input.getFileCode();
            }
        });
    }

}
